package com.sreader.logic;

import java.text.Normalizer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;


public class CalculLogic implements java.io.Serializable{
	
	
	/**
	 * 
	 */
	private static final long serialVersionUID = -4023270958877393238L;
	private String individumLanguageCode;
	private static final  int ACTUALBASE = 9;
	private static final int QUARTER =4;
	private static final int THIRD =3;
	private static final int DECIMALBASE = 10;
	private static final List<String> VOYELLES= new ArrayList<String>(Arrays.asList(
																	"A",
																	"E",
																	"I",
																	"O",						
																	"U"));
	private static final List<String> VOYELLESFR= new ArrayList<String>(Arrays.asList(
																		"A",
																		"E",
																		"I",
																		"O",						
																		"U",
																		"Y"));
	
	public CalculLogic(String languageCode){
		this.setIndividumLanguageCode(languageCode);
	}
	public CalculLogic(){
		
	}
	
	public String getIndividumLanguageCode() {
		return individumLanguageCode;
	}

	public void setIndividumLanguageCode(String individumLanguageCode) {
		this.individumLanguageCode = individumLanguageCode;
	}
	private  List<Integer> entry;
	

	/**
	 * CODE: NAbscents
	 * FORMULES:les Nombre de 1 à 9 qui n'apparaisent pas du tout
	 *          dans la mis en Nombre
	 * @param occurenceTable
	 * @return List liste de int
	 */
	public List<Integer> computeNombreManquant(OccurenceTable occurenceTable) {
		OccurenceTable table = new OccurenceTable();
		List<Integer> nombresManquants = new ArrayList<Integer>();
		table = occurenceTable;
		for(Integer key: table.getOccurenceTable().keySet()){
			if(table.getOccurenceTable().get(key) == 0){
				nombresManquants.add(key);
			}
		}
		System.out.println("nombresManquants " + nombresManquants + " ");
		return nombresManquants;
	}

	/**
	 * CODE: NExces
	 * FORMULE: Nombre representant plus du quart des lettres du Nom
	 * @param nameOccurenceTable
	 * @param nameCount
	 * @return List liste de  int
	 */
	public List<Integer> computeNombreEnExces(OccurenceTable nameOccurenceTable, int nameCount) {
		List<Integer> nombresEnExces = new ArrayList<Integer>();
		int nameCountQuater = nameCount / QUARTER;
		for(Integer key: nameOccurenceTable.getOccurenceTable().keySet()){
			int occurenceOfKey = nameOccurenceTable.getOccurenceTable().get(key);
			if(occurenceOfKey > nameCountQuater){
				nombresEnExces.add(key);
			}
		}
		return nombresEnExces;
	}
	
	/**
	 * CODE: NDominants
	 * FORMULE: Nombres les plus souvents dans la table d'occurence
	 * @param occurenceValues
	 * @return int
	 */
	public List<Integer> computeNombreDominant(OccurenceTable nameOccurenceTable){
		List<Integer> nombresDominants = new ArrayList<Integer>();
		for(Integer key: nameOccurenceTable.getOccurenceTable().keySet()){
			int occurenceOfKey = nameOccurenceTable.getOccurenceTable().get(key);
			if(occurenceOfKey > 0){
				nombresDominants.add(key);
			}
		}
		//person.setOccurenceTable(OccurenceTableUI.setOccurenceOne(value)
		return nombresDominants;
	}
	/**
	 * @param lettersMapping List<<Integer>>
	 * @return int
	 */
	public int miseEnNombre(List<Integer> lettersMapping){
		int racine = 0;
		Iterator<Integer> iterator = lettersMapping.iterator();
		int sum = 0;
		try{
			while(iterator.hasNext()){
				sum += iterator.next();
			}
			racine = sum % ACTUALBASE;
		}catch (Exception e) {
			// TODO: handle exception
			System.out.println("The operation could not be executed. " +
					"Please check if the list you are using is set" + e.getMessage());
		}
		
		if (racine == 0){
			return 9;
		}
		return racine;
	}

	/**
	 * @param lettersMapping List<<Integer>>
	 * @return int
	 */
	public int miseEnNombreOther(List<Integer> lettersMapping){
		int racine = 0;
		int nextRacine = 0;
		int nextSum = 0;
		Iterator<Integer> iterator = lettersMapping.iterator();
		try{
			while(iterator.hasNext()){
				nextSum += iterator.next();
				if(nextSum == 11 | nextSum == 22){
					nextRacine = nextSum ; 
				}else{
					nextRacine = nextSum % ACTUALBASE;
					
				}
				nextSum = nextRacine;				
			}
			racine = nextRacine;
		}catch (Exception e) {
			// TODO: handle exception
			System.out.println("The operation could not be executed. " +
					"Please check if the list you are using is set" + e.getMessage());
		}
		
		if (racine == 0){
			return 9;
		}
		return racine;
	}
	
	public int miseEnNombreNew(List<Integer> lettersMapping){
		int racine = 0;
		int sumTotal = 0;
		Iterator<Integer> iterator = lettersMapping.iterator();
		List<Integer> digitsOfSumTotal = new ArrayList<Integer>();
		try{
			while(iterator.hasNext()){
				sumTotal += iterator.next();	
			}
			/*do{
				digitsOfSumTotal.add(sumTotal % DECIMALBASE);
				sumTotal = sumTotal/ DECIMALBASE;
			}while((sumTotal % DECIMALBASE) != 0);
			System.out.println("chiffres " + digitsOfSumTotal);
			racine = miseEnNombreOther(digitsOfSumTotal);*/
			racine = getRacineOntologique(sumTotal);
		}catch (Exception e) {
			// TODO: handle exception
			System.out.println("The operation could not be executed. " +
					"Please check if the list you are using is set" + e.getMessage());
		}
		
		if (racine == 0){
			return 9;
		}
		System.out.println("racineNewVersion: " + racine);
		return racine;
	}
	
	public int getRacineOntologique(int value){
		int racine = 0;
		List<Integer> digitsOfSumTotal = new ArrayList<Integer>();
		do{
			digitsOfSumTotal.add(value % DECIMALBASE);
			value = value/ DECIMALBASE;
		}while((value % DECIMALBASE) != 0);
		racine = miseEnNombreOther(digitsOfSumTotal);
		return racine;
	}
	/**
	 * <pre>CODE: NE
	 * FORMULE: Réduction de la somme de NA + NH
	 * Autres Appelations:-Nombre d'expression
	 *                    -nombre de personnalité
	 * </pre>
	 * @param nombreaActif int
	 * @param nombreHereditaire int
	 * @return  int
	 */
	public int computeNombreDExpression(int nombreActif, int nombreHereditaire){
		int racine = 0;
		try{
			int totalNameFirstname = nombreActif + nombreHereditaire;
			racine = totalNameFirstname % ACTUALBASE;
		}catch (Exception e) {
			// TODO: handle exception
		}
		if(racine == 0){
			return 9;
		}
		return racine;
	}
	/**
	 * @param firstAndLastNameMapping
	 * @return int
	 */
	public int computeNombreDExpressionOther(List<Integer> firstAndLastNameMapping){
		
		return miseEnNombreNew(firstAndLastNameMapping);
	}
	/**
	 * <pre>
	 * CODE: NI
	 * FORMULE: Réduction de la somme des seules voyelles du prénom (NIa)  
	 *          + 
	 *          les seules voyelles du nom (NIb)
	 * Autres Appelations: -Premier nombre de chance personnelle
	 *                     -Nombre du coeur
	 *                     -Nombre d'idéalité ou élan personnel
	 * </pre>
	 * @param firstAndLastNames Nom + prénom
	 * @return int
	 */
	public int computeNombreIntime(List<String>  firstAndLastNames){
		int racine = 0;
		List<String> languageVoyelles = new ArrayList<String>();
		if(getIndividumLanguageCode().equalsIgnoreCase(new Locale("fr","").getLanguage())){
			languageVoyelles = VOYELLESFR;
		}else{
			languageVoyelles = VOYELLES;
		}
		//System.out.println("languageCode " + getIndividumLanguageCode());
		List<String>voyelles = new ArrayList<String>();
		List<Integer>voyellesMapping = new ArrayList<Integer>();
		for(String letter: firstAndLastNames){
			if(languageVoyelles.contains(unAcentString(letter))){
				voyelles.add(letter);
			}
		}
		System.out.println("Voyelles : " + voyelles);
		voyellesMapping = lettersToDigitMapping(voyelles);
		racine = miseEnNombreNew(voyellesMapping);
		return racine;
	}
	/**
	 * <pre>
	 *CODE: NR
	 *FORMULE : 
	 * 	1. NE - NI
	 *	2. Réduction des seules consonnes du nom + prénoms
	 *Autres Appelations:-Deuxième nombre de chance personnelle
	 *                   -Nombre de personnalité
	 * </pre>
	 * @param  firstAndLastNames List liste de String
	 * @return int
	 */
	public int computeNombreDeRealisation(List<String> firstAndLastNames){
		int racine = 0;
		List<String>consonnes = new ArrayList<String>();
		List<Integer>consonnesMapping = new ArrayList<Integer>();
		List<String> languageVoyelles = new ArrayList<String>();
		if(getIndividumLanguageCode().equalsIgnoreCase(new Locale("fr","").getLanguage())){
			languageVoyelles = VOYELLESFR;
		}else{
			languageVoyelles = VOYELLES;
		}
		System.out.println("FirstAndLastNames : " + firstAndLastNames);
		for(String letter: firstAndLastNames){
			if(!languageVoyelles.contains(unAcentString(letter))){
				consonnes.add(letter);
			}
		}
		System.out.println("Consonnes : " + consonnes);
		consonnesMapping = lettersToDigitMapping(consonnes);
		racine = miseEnNombreNew(consonnesMapping);
		return racine;
	}
	/*public int computeNombreDesInitiales(List<String> firstNames, List<String>lastNames){
		int racine=0;
		
		return racine;
	}*/
	/**
	 * Remplace chaque lettre par son chiffre correspondant
	 * @param letters List liste de String
	 * @return List liste de int
	 */
	public List<Integer> lettersToDigitMapping(List<String> letters){
		List<Integer> digits= new ArrayList<Integer>();
		Iterator<String> lettersIterator = letters.iterator();
		LetterToDigitMapping letterToDigit = new LetterToDigitMapping();
		while(lettersIterator.hasNext()){
			String letter = lettersIterator.next();
			digits.add(letterToDigit.findDigitForLetter(unAcentString(letter)));
		}
		return digits;
	}
	/**
	 * Returns the first letters of a given list of element separated 
	 * with the empty string character ("")
	 * @param letters List liste de String
	 * @return List  liste de String
	 */
	public List<String> computeInitiales(List<String> letters){
		List<String>initiales = new ArrayList<String>();
		initiales.add(unAcentString(letters.get(0)));
		for(int i = 1; i<(letters.size()-1); i++){
			int j= i+1;
			if(letters.get(i)=="" &  letters.get(j)!=null){
				initiales.add(unAcentString(letters.get(j)));
			}
		}
		return initiales;
	}
	/**
	 * <pre>
	 * Retourne
	 * CODE:
	 * FORMULE: reste de la division (anneEtudie - anneeNaissance)/lnc
	 * </pre>
	 * @param anneeEtudie int année étudiée
	 * @param anneNaissance int année de naissance
	 * @param lnc nombre de lettres du nom complet (nom+prénom)
	 * @return int
	 */
	public int computeLettreDePassage(Object anneeEtudie, List<Integer> anneNaissance, int lnc){
		int year = 0;
		if(anneeEtudie == null){
			Calendar cal = Calendar.getInstance();
			year = cal.get(Calendar.YEAR);
		}else{
			year = Integer.parseInt(anneeEtudie.toString());
		}
		int dateOfBirthAsInteger = computeDateComponent(anneNaissance);
		int lettre = ((year - dateOfBirthAsInteger) % lnc)+1;
		System.out.println("lettre de  passage: " + lettre);
		return ((year - dateOfBirthAsInteger) % lnc)+1;	
	}
	/**
	 * @param anneeNaissance
	 * @return an int
	 */
	public int computeVibrationAnnee(int anneeNaissance){
		Calendar cal = Calendar.getInstance();
		int year = cal.get(Calendar.YEAR);
		int resultat = ((year - anneeNaissance) % 12) +1;
		if(resultat > 12){
			resultat = resultat % 12;
		}
		return resultat;
	}
	/**
	 * <pre>
	 * CODE: NAP
	 * FORMULE: 
	 * </pre>
	 * @param annee L'anné étudiee
	 * @param jourNaissance
	 * @param moisNaissance
	 * @return an int
	 */
	public int computeNombreAnneePersonelle(Object annee, int jourNaissance, int moisNaissance){
		int year = 0;
		if(annee == null){
			Calendar cal = Calendar.getInstance();
			year = cal.get(Calendar.YEAR);
		}else{
			year = Integer.parseInt(annee.toString());
		}
		System.out.println("Annee: " + year);
		return ((year + jourNaissance + moisNaissance) % 9);
	}
	/**
	 * <pre>
	 * CODE:NMP
	 * FORMULE: racine(NPA + moisetudie)
	 * 
	 * </pre>
	 * @param nombreAnneePersonelle
	 * @param mois
	 * @return an int
	 */
	public int computeNombreMoisPersonnelle( Object nombreAnneePersonnelle, Object mois){
		int year = 0;
		int month = 0;
		if(nombreAnneePersonnelle == null){
			Calendar cal = Calendar.getInstance();
			year = cal.get(Calendar.YEAR);
		}else{
			year = Integer.parseInt(nombreAnneePersonnelle.toString());
		}
		if(mois == null){
			Calendar cal = Calendar.getInstance();
			month= cal.get(Calendar.MONTH) + 1;
		}else{
			month = Integer.parseInt(mois.toString());
		}
		System.out.println("Mois: " + month);
		return (month + year) % 9 ==0 ? 9 : (month + year) % 9;
	}
	/**CODE: NJP
	 * FORMULE: racine(NMP+ jour)
	 * @param nombreMoisPersonnelle
	 * @param mois
	 * @return an int
	 */
	public int computeNombreJourPersonnel(Object nombreMoisPersonnel, Object jour){
		int day =0;
		int month=0;
		if(nombreMoisPersonnel ==null){
			Calendar cal = Calendar.getInstance();
			month= cal.get(Calendar.MONTH) + 1;
		}else{
			month = Integer.parseInt(nombreMoisPersonnel.toString());
		}
		if(jour == null){
			Calendar cal = Calendar.getInstance();
			day= cal.get(Calendar.DAY_OF_MONTH);
		}else{
			day = Integer.parseInt(jour.toString());
		}
		System.out.println("Jour: " + day);
		return (day + month) % 9 ==0 ? 9 : (day + month) % 9;
	}
	/**
	 * Unacent a given character
	 * È => E; À=>A etc
	 * @param letter String
	 * @return String
	 */
	private String unAcentString(String letter){
		String temp = Normalizer.normalize(letter, Normalizer.Form.NFD);
		Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");
	return pattern.matcher(temp).replaceAll("");	
	}
	
	/**
	 * @param lettersList List<<String>>
	 * @return List<<String>>
	 */
	public List<String>normalizeLettersList(List<String> lettersList){
		boolean isStartFlagOn = true;
		boolean isEndFlagOn = false;
		ArrayList<String> entry = new ArrayList<String>();
		
		for(String letter: lettersList){
			if(letter!=null & isStartFlagOn==true & isEndFlagOn == false){
				entry.add(letter);
				isStartFlagOn = true;
				isEndFlagOn = false;
			}
			else if(letter == null & isStartFlagOn == true & isEndFlagOn==false){
				//end of Entry			
				isStartFlagOn =false;
				isEndFlagOn = true;
				entry.add("");
				
			}else if(letter!=null & isStartFlagOn ==false & isEndFlagOn == true){
				//new entry
				isStartFlagOn = true;
				isEndFlagOn = false;
				entry.add(letter);
			}
		}
		return entry;
	}
	
	/**calcul la table d'occurence sur la base des noms et prénoms
	 * @param lettersMapping List<<Integer>>
	 * @return OccurenceTableUI
	 */
	public OccurenceTable computeOccurenceTable(List <Integer> lettersMapping){
		Iterator<Integer> intIterator = lettersMapping.iterator();
		OccurenceTable occurencetable = new OccurenceTable();
		while(intIterator.hasNext()){
			int digit = intIterator.next();
			switch (digit) {
			case 1:
				occurencetable.setOccurenceOne(occurencetable.getOccurenceOne() + 1);
				break;
			case 2:
				occurencetable.setOccurenceTwo(occurencetable.getOccurenceTwo() + 1);
				break;
			case 3:
				occurencetable.setOccurenceThree(occurencetable.getOccurenceThree() + 1);
				break;
			case 4:
				occurencetable.setOccurenceFour(occurencetable.getOccurenceFour() + 1);
				break;
			case 5:
				occurencetable.setOccurenceFive(occurencetable.getOccurenceFive() + 1);
				break;
			case 6:
				occurencetable.setOccurenceSix(occurencetable.getOccurenceSix() + 1);
				break;
			case 7:
				occurencetable.setOccurenceSeven(occurencetable.getOccurenceSeven() + 1);
				break;
			case 8:
				occurencetable.setOccurenceEight(occurencetable.getOccurenceEight() + 1);
				break;
			case 9:
				occurencetable.setOccurenceNine(occurencetable.getOccurenceNine() + 1);
				break;

			default:
				break;
			}
		}
		System.out.println("occurenceTable" + occurencetable.getOccurenceTable());
		return occurencetable;
	}
	
	
	/**
	 * @param entryMapping List<<Integer>>
	 * @return List<<Integer>>
	 */
	public List<Integer> stripEntriesMapping(List<Integer> entryMapping ){
		this.entry = new ArrayList<Integer>();
		
		return entry;
	}
	/**
	 * <pre>
	 * CODE: CV
	 * FORMULE:
	 * Réduction de la date de naissance
	 * jour + mois + année
	 * Autres Appelations:Nombre de destiné ou nombre
	 *                    Nombre de l'enseignement
	 * </pre>
	 * @param dateOfBirth the date of birth as a list of digits
	 */
	public int computeCheminDeVie(List<Integer> dateOfBirth){
		return miseEnNombreNew(dateOfBirth);
	}
	/**
	 * <pre>
	 * CODE: NDI
	 * FORMULE: Réduction de la somme des valeurs des initiales
	 *          (nom+prénoms)
	 * @param initialesLettersMapping list des itiniales mis en nombre
	 */
	public int computeNombreDesInitiales(List<Integer> initialesLettersMapping){
		return miseEnNombreNew(initialesLettersMapping);
	}
	/**
	 * <pre>
	 * Nombre tiré du nom + date de naissance
	 * CODE: NU
	 * FORMULE:Réduction de  la somme 
	 *         NE + CV
	 * Autres Appelations: Nombre superbénéfique
	 * </pre>
	 * @param nombreDExpression int
	 * @param cheminDeVie int
	 * @return int
	 */
	public int computeNombreUnique(int nombreDExpression,int cheminDeVie){
		List<Integer> nombresList = new ArrayList<Integer>();		
		//JDF
		/*
		 int nombres = nombreDExpression + cheminDeVie;
		 if(nombres > 22){
			nombresList.add(nombreDExpression);
			nombresList.add(cheminDeVie);
			 return miseEnNombre(nombresList);
		}else{
			return nombres;
		}*/
		
		//JPK
		nombresList.add(nombreDExpression);
		nombresList.add(cheminDeVie);
		return miseEnNombreNew(nombresList);
	}
	/**
	 * Retourne la valeur numérique du composant de date entrée
	 * @param dateComponentMapping List liste des élements du composant de date
	 * @return int le componsant 
	 */
	public int computeDateComponent(List<Integer> dateComponentMapping){
		System.out.println("dateComponent= " + dateComponentMapping);
		int dateComponentMappingAsInt = 0;
		int multiplicator = 10;
		for(int i = 0; i < dateComponentMapping.size(); i++){
			//exposant de départ = longeurArrayList - 1 (pour year: 0,1,2,3)
			int startExp = dateComponentMapping.size() - 1;
			dateComponentMappingAsInt += dateComponentMapping.get(i) * Math.pow(multiplicator, (startExp - i));
		
		}
		return dateComponentMappingAsInt;
		
	}
	/**
	 * Retourne la valeur du composant de l'identité
	 * @param identityComponent List liste des élements du composant de l'identité
	 * @return String
	 */
	public String computeIdentityComponent(List<String> identityComponent){
		//System.out.println("valeurAvant: " + identityComponent);
		String identityComponentAsString ="";
		for(int i=0; i<identityComponent.size();i++){
			//System.out.println("lettres: " + identityComponent.get(i));
			identityComponentAsString+=identityComponent.get(i);
			//System.out.println("lettresApres: " + identityComponentAsString);
		}
		//System.out.println("valeurApres: " + identityComponentAsString);
		return  identityComponentAsString.trim();
	}
	/**
	 * Retourne le nombre de lettres qu contient une entité
	 * de noms, prénoms ou nom + prénoms
	 * @param lettersMapping
	 * @return
	 */
	public int getNombreDeLettre(List<Integer> lettersMapping){
		int nombreDeLettres = 0;
		for(int i = 0; i<lettersMapping.size();i++){
			if(lettersMapping.get(i) != 0)
				nombreDeLettres++;
		}
		System.out.println("lnc= " + nombreDeLettres);
		return nombreDeLettres;
	}
	
}
